﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using AEM.Data;
using AEM.Utils;

namespace AEM.Common
{
    public class SerialPortManager : IDisposable
    {
        private SerialPort _serialPort;

        public event SerialPortDataReceivedEventHandler NewSerialDataRecieved;

        public event SameDataEventHandler SameDataEvent;
        public event EventHandler SendCommandEvent;
        private bool ReceiveEventFlag;//接收事件是否有效
        private byte[] receiveBuffer = new byte[75];
        static object loglocker = new object();

        public SerialPortManager()
        {
        }
        public SerialPortManager(string PortName)
        {
            this.PortName = PortName;
            _serialPort = new SerialPort(this.PortName, this.BaudRate);
            _serialPort.DataBits = this.DataBits;
            _serialPort.StopBits = this.StopBits;
            _serialPort.RtsEnable = this.RtsEnable;
            _serialPort.ReceivedBytesThreshold = 1;
        }

        /// <summary>
        /// Connects to a serial port defined through the current settings
        /// </summary>
       /* public void StartListening()
        {
            // Closing serial port if it is open
            if (_serialPort != null && _serialPort.IsOpen)
                _serialPort.Close();

            // Setting serial port settings
            _serialPort = new SerialPort(this.PortName, this.BaudRate);
            _serialPort.Parity = Parity.None;
            _serialPort.DataBits = this.DataBits;
            _serialPort.StopBits = this.StopBits;
            _serialPort.RtsEnable = this.RtsEnable;
            _serialPort.ReceivedBytesThreshold = 1;

            // Subscribe to event and open serial port for data
            _serialPort.DataReceived += new SerialDataReceivedEventHandler(_serialPort_DataReceived);
            _serialPort.Open();
        }*/

        public void StartListening(Data.Enum.LiftType liftType)
        {
            // Closing serial port if it is open
            if (_serialPort != null && _serialPort.IsOpen)
                _serialPort.Close();

            // Setting serial port settings
            if (liftType == Data.Enum.LiftType.扶梯)
            {
                _serialPort = new SerialPort(this.PortName, 9600);
                _serialPort.Parity = Parity.Even;
            }
            else
            {
                _serialPort = new SerialPort(this.PortName, this.BaudRate);
                _serialPort.Parity = Parity.None;
            }

            _serialPort.DataBits = this.DataBits;
            _serialPort.StopBits = this.StopBits;
            _serialPort.RtsEnable = this.RtsEnable;
            _serialPort.ReceivedBytesThreshold = 1;

            // Subscribe to event and open serial port for data
            _serialPort.DataReceived += new SerialDataReceivedEventHandler(_serialPort_DataReceived);
            _serialPort.Open();
        }

        public void SendCommand(byte[] data)
        {
            try
            {
                if (_serialPort != null && !_serialPort.IsOpen)
                    _serialPort.Open();
                _serialPort.DiscardOutBuffer();
                _serialPort.ReceivedBytesThreshold = 1;
                _serialPort.Write(data, 0, data.Length);
                //LogPrint(this.PortName + "[Send]:" + data.ToArray().toString<byte>());
                //Log.WriteInf(ConsoleColor.White, this.PortName + "[Send]:" + data.ToArray().toString<byte>());
            }
            catch
            {
                //Log.WriteError(ex.Message, ex);
                //Thread.Sleep(10);
            }
        }

        public static void LogPrint(string message)
        {
            try
            {
                lock (loglocker)
                {
                    string dirpath = AppDomain.CurrentDomain.BaseDirectory + @"/Log";
                    string filepath = dirpath + "/Log_" + DateTime.Now.ToString("yyyy-MM-dd") + ".txt";


                    //生成目录
                    //创建文件夹
                    if (Directory.Exists(dirpath) == false)//如果不存在就创建file文件夹
                    {
                        Directory.CreateDirectory(dirpath);
                    }

                    // 判断文件是否存在，不存在则创建，否则读取值显示到txt文档
                    if (!System.IO.File.Exists(filepath))
                    {
                        FileStream fs1 = new FileStream(filepath, FileMode.Create, FileAccess.Write);//创建写入文件 
                        fs1.Close();
                    }

                    File.AppendAllText(filepath, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + ":" + message + "\r\n");
                }
            }
            catch
            {

            }
        }
        public void SendCommand(byte[] data, bool isTimeOut)
        {
            if (isTimeOut)
            {
                //_serialPort.DiscardInBuffer();
                ReceiveEventFlag = false;
                Log.WriteInf(ConsoleColor.Magenta, this.PortName + ":【TimeOut】" + "[Send]:" + data.ToArray().toString<byte>());
            }
            SendCommand(data);
        }

        List<byte> TmpDataReceived = new List<byte>();

        void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            //_serialPort.ReceivedBytesThreshold = 10000;
            if (ReceiveEventFlag)
            {
                //_serialPort.ReceivedBytesThreshold = 1;
                return;
            }
            ReceiveEventFlag = true;
            int lenght = _serialPort.BytesToRead;
            byte[] tmpbyte = new byte[lenght];
            try
            {
                for (int i = 0; i < lenght; i++)
                {
                    tmpbyte[i] = (byte)_serialPort.ReadByte();
                }
                TmpDataReceived.AddRange(tmpbyte);
                //LogPrint("收到数据：" + ToHexs(tmpbyte));
            }
            catch { }
            if (TmpDataReceived.Count > 500)
            {
                TmpDataReceived.RemoveRange(0, TmpDataReceived.Count);
                //LogPrint(this.PortName + ":数据接收池清理");
#if DEBUG
                Log.WriteInf(ConsoleColor.Blue, this.PortName + ":数据接收池清理");
               
#endif
            }

            while (true)
            {
                string type = _serialPort.PortName;
                if (_serialPort.PortName.ToUpper()=="COM1")
                {
                    int ft_start = -1, ft_end = -1;
                    if (TmpDataReceived.Count >= 49)
                    {
                        try
                        {
                            ft_start = TmpDataReceived.FindIndex(x => x == 0x2C);
                            if (ft_start == -1)
                            {
                                TmpDataReceived.RemoveRange(0, TmpDataReceived.Count - 1);
                                break;
                            }
                            if (ft_start > 1 && TmpDataReceived[ft_start - 1] == 0x03)
                            {
                                if (TmpDataReceived.Count - ft_start >= 47)
                                {
                                    ft_end = ft_start + 47;
                                    receiveBuffer = new byte[47];
                                    Array.Copy(TmpDataReceived.ToArray(), ft_start - 2, receiveBuffer, 0, 47);
                                    byte[] crc = Crc18(receiveBuffer);
                                    if (crc[1] == TmpDataReceived[ft_start + 45] && crc[0] == TmpDataReceived[ft_start + 46])
                                    {
                                        byte[] receive = new byte[49];
                                        for (int i = 0; i < receiveBuffer.Length; i++)
                                        {
                                            receive[i] = receiveBuffer[i];
                                        }
                                        receive[47] = crc[1];
                                        receive[48] = crc[0];
                                        SendCommandEvent.Invoke(this, new EventArgs());
                                        NewSerialDataRecieved.BeginInvoke(this, new SerialDataEventArgs(receive, "ft"), new AsyncCallback(CallBack), null);
                                        TmpDataReceived.RemoveRange(0, ft_end);
                                    }
                                    else
                                    {
                                        TmpDataReceived.RemoveRange(0, ft_start + 1);
                                    }
                                }
                                else
                                {
                                    TmpDataReceived.RemoveRange(0, ft_start + 1);
                                }
                            }
                            else
                            {
                                Log.WriteInf(ConsoleColor.Cyan, this.PortName + "start=-1:" + TmpDataReceived.ToArray().toString<byte>(" "));
                                //TmpDataReceived = new List<byte>();
                                TmpDataReceived.RemoveRange(0, ft_start + 1);
                            }
                        }
                        catch (Exception ex)
                        {
                            if (TmpDataReceived.Count > 0)
                            {
                                //TmpDataReceived.RemoveAt(ft_start + 1);
                                TmpDataReceived.RemoveRange(0, 1);
                            }
                        }
                        //try
                        //{
                        //    start = TmpDataReceived.FindIndex(x => x == 0xA1);
                        //    if (-1 != start)
                        //    {
                        //        end = start + 74;
                        //        if (end <= TmpDataReceived.Count - 1)
                        //        {
                        //            if (TmpDataReceived[end] == 0xD1)
                        //            {
                        //                //bool flag = false;

                        //                receiveBuffer = new byte[end - start + 1];
                        //                Array.Copy(TmpDataReceived.ToArray(), start, receiveBuffer, 0, end - start + 1);
                        //                //if (Checkout(receiveBuffer))
                        //                //{
                        //                // flag = true;
                        //                SendCommandEvent.Invoke(this, new EventArgs());
                        //                NewSerialDataRecieved.BeginInvoke(this, new SerialDataEventArgs(receiveBuffer, ""), new AsyncCallback(CallBack), null);
                        //                //  Log.WriteInf(ConsoleColor.Cyan, this.PortName + " 接收正确end-start==74:" + TmpDataReceived.GetRange(0, end + 1).ToArray().toString<byte>(" "));


                        //                //}
                        //                TmpDataReceived.RemoveRange(0, end + 1);


                        //            }
                        //            else
                        //            {
                        //                TmpDataReceived.RemoveRange(0, start + 1);
                        //            }

                        //        }
                        //        else
                        //        {
                        //            TmpDataReceived.RemoveRange(0, start);
                        //        }

                        //    }
                        //    else
                        //    {
                        //        ft_start = TmpDataReceived.FindIndex(x => x == 0x2C);
                        //        if (ft_start > 1 && TmpDataReceived[ft_start - 1] == 0x03)
                        //        {
                        //            receiveBuffer = new byte[49];
                        //            Array.Copy(TmpDataReceived.ToArray(), ft_start - 2, receiveBuffer, 0, 49);
                        //            SendCommandEvent.Invoke(this, new EventArgs());
                        //            NewSerialDataRecieved.BeginInvoke(this, new SerialDataEventArgs(receiveBuffer, "ft"), new AsyncCallback(CallBack), null);
                        //            TmpDataReceived.RemoveRange(0, 49);
                        //        }
                        //        else
                        //        {
                        //            Log.WriteInf(ConsoleColor.Cyan, this.PortName + "start=-1:" + TmpDataReceived.ToArray().toString<byte>(" "));
                        //            TmpDataReceived = new List<byte>();
                        //            TmpDataReceived.RemoveRange(0, ft_start);
                        //        }
                        //    }

                        //    //  if (TmpDataReceived.Count < 75)
                        //    // break;

                        //}
                        //catch (Exception ex)
                        //{
                        //    if (TmpDataReceived.Count > 0)
                        //    {
                        //        TmpDataReceived.RemoveAt(start + 1);
                        //    }
                        //}
                        //// }


                    }
                    else
                        break;
                }
                else
                {
                    int start = -1, end = -1;
                    if (TmpDataReceived.Count >= 75)
                    {
                        try
                        {
                            start = TmpDataReceived.FindIndex(x => x == 0xA1);
                            if (-1 != start)
                            {
                                end = start + 74;
                                if (end <= TmpDataReceived.Count - 1)
                                {
                                    if (TmpDataReceived[end] == 0xD1)
                                    {
                                        receiveBuffer = new byte[end - start + 1];
                                        Array.Copy(TmpDataReceived.ToArray(), start, receiveBuffer, 0, end - start + 1);
                                        SendCommandEvent.Invoke(this, new EventArgs());
                                        NewSerialDataRecieved.BeginInvoke(this, new SerialDataEventArgs(receiveBuffer,""), new AsyncCallback(CallBack), null);
                                        TmpDataReceived.RemoveRange(0, end + 1);
                                    }
                                    else
                                    {
                                        TmpDataReceived.RemoveRange(0, start + 1);
                                    }
                                }
                                else
                                {
                                    TmpDataReceived.RemoveRange(0, start);
                                }
                            }
                            else
                            {
                                Log.WriteInf(ConsoleColor.Cyan, this.PortName + "start=-1:" + TmpDataReceived.ToArray().toString<byte>(" "));
                                TmpDataReceived = new List<byte>();
                            }
                        }
                        catch
                        {
                            if (TmpDataReceived.Count > 0)
                            {
                                TmpDataReceived.RemoveAt(start + 1);
                            }
                        }
                    }
                    else
                        break;
                }
            }
            _serialPort.ReceivedBytesThreshold = 1;
            ReceiveEventFlag = false;
            return;
        }

        public string ToHexs(byte[] data)
        {
            if (data.Length <= 0) return "";
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < data.Length; i++)
            {
                sb.Append(data[i].ToString("X2"));
            }
            string str = sb.ToString();
            return str;
        }

        #region CRC校验
        public byte[] Crc18(byte[] buffer, int start = 0, int len = 0)
        {
            if (buffer == null || buffer.Length == 0) return null;
            if (start < 0) return null;
            if (len == 0) len = buffer.Length - start;
            int length = start + len;
            if (length > buffer.Length) return null;
            ushort crc = 0xFFFF;// Initial value
            for (int i = start; i < length; i++)
            {
                crc ^= buffer[i];
                for (int j = 0; j < 8; j++)
                {
                    if ((crc & 1) > 0)
                        crc = (ushort)((crc >> 1) ^ 0xA001);// 0xA001 = reverse 0x8005
                    else
                        crc = (ushort)(crc >> 1);
                }
            }
            byte[] ret = BitConverter.GetBytes(crc);
            Array.Reverse(ret);
            return ret;
        }
        #endregion

        private void CallBack(IAsyncResult tag)
        {
            //  IAsyncResult实际上就是AsyncResult对象，
            //  取得它也就可以从中取得用于调用函数的委托对象
            AsyncResult result = (AsyncResult)tag;

            //  取得委托
            SerialPortDataReceivedEventHandler del = (SerialPortDataReceivedEventHandler)result.AsyncDelegate;

            //  取得委托后，我们需要在其上执行EndInvoke。
            //  这样就可以取得函数中的执行结果。
            del.EndInvoke(tag);

        }

        private bool Checkout(byte[] bytes)
        {
            int datalen = 0; int slen = 0;
            try
            {
                #region 第3--4位：  datalen；    描述：data数据总字节数（Data的字节总数）,即m的值
                datalen = int.Parse(bytes[3].ToString("X2") + bytes[4].ToString("X2"));
                #endregion
                #region 第5位：    slen    描述：Len3:S数据总字节数，即n的值
                slen = int.Parse(bytes[5].ToString("X2"));
                #endregion
                if (bytes.Length < 19 + datalen + slen)
                {
                    Log.WriteLog(Log.LogType.Error, this.PortName + ":长度不对(bytes[3]bytes[4]bytes[5]=" +
                        bytes[3].ToString("0x00") + bytes[4].ToString("0x00") + bytes[5].ToString("0x00") + "<>" +
                        (datalen + slen) + ")" + bytes.toString<byte>(" "));
                    return false;
                }
                if (bytes[19 + datalen + slen - 1] != 0xD1)
                {
                    Log.WriteLog(Log.LogType.Error, "---数据尾不对---");
                    return false;
                }
                int checksum = 0;
                for (int i = 1; i < 19 + datalen + slen - 2; i++)
                {
                    checksum += int.Parse(bytes[i].ToString("X2"), NumberStyles.HexNumber);
                }
                if (checksum >= 128) checksum %= 128;
                /*if (bytes[19 + datalen + slen - 2] != checksum)
                {
                    Log.WriteLog(Log.LogType.Error, this.PortName + ":校验出错(bytes[73]=" + bytes[73] + "<>" +
                        checksum + ")" + bytes.toString<byte>(" "));
                    return false;
                }*/
            }
            catch { }
            return true;
        }

        /// <summary>
        /// Closes the serial port
        /// </summary>
        public void StopListening()
        {
            if (ReceiveEventFlag)
            {
                StopListening();
                return;
            }
            _serialPort.Close();
        }

        ~SerialPortManager()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
        }
        // Part of basic design pattern for implementing Dispose
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                _serialPort.DataReceived -= new SerialDataReceivedEventHandler(_serialPort_DataReceived);
            }
            // Releasing serial port (and other unmanaged objects)
            if (_serialPort != null)
            {
                if (_serialPort.IsOpen)
                    _serialPort.Close();

                _serialPort.Dispose();
            }
        }

        private string _portname = SerialPort.GetPortNames()[0];
        public string PortName
        {
            get { return this._portname; }
            set { _portname = value; }
        }
        private int _baudrate = 19200;
        public int BaudRate
        {
            get { return this._baudrate; }
            set { this._baudrate = value; }
        }
        private int _databits = 8;
        public int DataBits
        {
            get { return this._databits; }
            set { _databits = value; }
        }
        private StopBits _stopbits = StopBits.One;
        public StopBits StopBits
        {
            get { return this._stopbits; }
            set { _stopbits = value; }
        }
        private bool _rtsenable = true;
        public bool RtsEnable
        {
            get { return this._rtsenable; }
            set { _rtsenable = value; }
        }
        public int Index { get; set; }

        internal void Clear()
        {
            _serialPort.DiscardOutBuffer();
            _serialPort.DiscardInBuffer();
        }
    }
}
